
#include "RobboLito.h"
#include "RobboTotalBase.h"

#define TOTAL_CACHE_PER 65536
static uint8 ***TotalBaseCache = NULL;
uint8 *BLOCCO; /* SMP ? */ /* need 1MB+128 for hiperindici */
static volatile uint64 *CACHE_INFO[4]; /* volatile? */
static uint64 TB_CACHE_COUNT = 0;
static MUTEX_TYPE SCOP_PILA_LOCK[1];

int SetTotalBaseCache (int mb)
{
  int i, j;
  mb = 1 << BSR (mb);
  TB_CACHE_COUNT = mb << 2;
  for (i = 0; i < 4; i++)
    if (CACHE_INFO[i])
      free (CACHE_INFO[i]);
  for (i = 0; i < 4; i++)
    if (TotalBaseCache[i])
      free (TotalBaseCache[i]);  
  CACHE_INFO[0] = malloc (TB_CACHE_COUNT * sizeof (uint64));
  CACHE_INFO[1] = malloc (TB_CACHE_COUNT * sizeof (uint64));
  CACHE_INFO[2] = malloc (TB_CACHE_COUNT * sizeof (uint64));
  CACHE_INFO[3] = malloc (TB_CACHE_COUNT * sizeof (uint64));
  TotalBaseCache[0] = malloc (TB_CACHE_COUNT * sizeof (uint8 *));
  TotalBaseCache[1] = malloc (TB_CACHE_COUNT * sizeof (uint8 *));
  TotalBaseCache[2] = malloc (TB_CACHE_COUNT * sizeof (uint8 *));
  TotalBaseCache[3] = malloc (TB_CACHE_COUNT * sizeof (uint8 *));
  for (j = 0; j < 4; j++)
    for (i = 0; i < TB_CACHE_COUNT; i++)
      TotalBaseCache[j][i] = NULL;
  for (j = 0; j < 4; j++)
    for (i = 0; i < TB_CACHE_COUNT; i++)
      CACHE_INFO[j][i] = 0xffffffff;
  printf ("TotalBaseCache is %dmb + (1mb)\n", mb);
  CURRENT_TOTAL_BASE_CACHE = mb;
  return mb;
}

void InitInitTotalBaseCache (uint64 mb)
{
  LOCK_INIT (SCOP_PILA_LOCK);
  TotalBaseCache = malloc (4 * sizeof (uint8 **));
#if 0
  BLOCCO = malloc (TOTAL_CACHE_PER); /* nein hiperindici */
#else
  BLOCCO = malloc (128 + 16 * TOTAL_CACHE_PER); /* for hiperindici */
#endif
  CACHE_INFO[0] = CACHE_INFO[1] = CACHE_INFO[2] = CACHE_INFO[3] = NULL;
  TotalBaseCache[0] = TotalBaseCache[1] =
    TotalBaseCache[2] = TotalBaseCache[3] = NULL;
  SetTotalBaseCache (mb);
}

#define GetCache0(a, b) ( (123 * (a) + 321 * (b) ) & ( TB_CACHE_COUNT - 1) )
#define GetCache1(a, b) ( (179 * (a) + 557 * (b) ) & ( TB_CACHE_COUNT - 1) )
#define GetCache2(a, b) ( (671 * (a) + 409 * (b) ) & ( TB_CACHE_COUNT - 1) )
#define GetCache3(a, b) ( (773 * (a) + 187 * (b) ) & ( TB_CACHE_COUNT - 1) )
#define GetCache(x ,a, b) \
  ((x) >= 2) ? (((x) == 2) ? GetCache2 (a, b) : GetCache3 (a, b)) \
             : (((x) == 1) ? GetCache1 (a, b) : GetCache0 (a, b))
static uint32 espul_conto = 0;
static int fc = 0;
static uint8 scopPILA (RobboTotalBase* tb, uint64 PositionCount)
{
  uint64 n = tb->num, zo = PositionCount / TOTAL_CACHE_PER, ci = (n << 32) | zo, ZO;
  int h[4], e, i;
  uint8* BPTR;
  uint8 u;
 PRIM:
  for (i = 0; i < 4; i++)
    {
      h[i] = GetCache (i, n, zo);
      if (CACHE_INFO[i][h[i]] == ci)
	{
	  u = TotalBaseCache[i][h[i]][PositionCount % TOTAL_CACHE_PER];
	  if (CACHE_INFO[i][h[i]] == ci)
	    return u;
	}
    }
  if (TRY_LOCK (SCOP_PILA_LOCK)) /* 0 upon nonLOCKed */
    {
      NANO_SLEEP (1000000);
      goto PRIM;
    }
  espul_conto++;
  e = -1;
  for (i = 0; i < 4; i++)
    if (CACHE_INFO[i][h[i]] == 0xffffffff)
      e = i;
  if (e == -1)
    {
      e = 0;
      for (i = 0; i < 16; i++)
	e += (espul_conto >> i);
      e &= 3;
    }
  if (!TotalBaseCache[e][h[e]])
    TotalBaseCache[e][h[e]] = malloc (TOTAL_CACHE_PER);
  CACHE_INFO[e][h[e]] = 0xfffeefff; /* invalidate */
  if (tb->BLOCCO_pro == 0x100000) /* 6 pezzo hiperindici */
    {
      ZO = zo & 0xf;
      zo >>= 4;
    }
  if (tb->TYPE == DISCO_COMPRESSO || tb->TYPE == DISCO_HUFFMAN)
    {
      uint32 ba = tb->indici[zo];
      sint32 lun = tb->indici[zo + 1] - ba; /* 32-bit plus under Windows ? */
      uint64 f = 0;
      uint64 pr = ba;
      char STR[1024];
      while (zo >= tb->supp_indice[f])
	f++;                           
      pr += (f << 32);
      if (!tb->Fdata)
	{
	  fc++;
	  if (fc == MASSIMO_FOPEN) /* FULL */
	    {
	      for (i = 0; i < MAX_TABLES; i++)
		if ((TABLE_TOTAL_BASES + i)->Fdata)
		  {
		    fclose ((TABLE_TOTAL_BASES + i)->Fdata);
		    (TABLE_TOTAL_BASES + i)->Fdata = NULL;
		  }
	      fc = 1;
	    }
	  sprintf (STR, "%s%s%s", tb->DIR_NOME,
		   COMPRESSA_PREFISSO + 1, tb->string);
	  tb->Fdata = fopen (STR, "rb");
	}
      fseek (tb->Fdata, pr, 0);
      fread (BLOCCO, 1, lun, tb->Fdata);
      if (tb->BLOCCO_pro == 0x100000) /* 6 pezzo hiperindici */
	{
	  BPTR = BLOCCO + ((uint32*) (BLOCCO + (8 * ZO)))[0];
	  lun = ((uint32*) (BLOCCO + (8 * ZO + 4)))[0];
	}
      else BPTR = BLOCCO;
      blocco_decompressa (TotalBaseCache[e][h[e]], BPTR, lun, tb->TYPE);
    }
  CACHE_INFO[e][h[e]] = ci; /* ritard a tiempo */
  u = TotalBaseCache[e][h[e]][PositionCount % TOTAL_CACHE_PER];
  UNLOCK (SCOP_PILA_LOCK);
  return u;
}

int tot_valu (RobboTotalBase* tb, uint64 PositionCount) /* SMP the this */
{
  uint8 u;
  u = scopPILA (tb, PositionCount);
  return u;
}

void re_fetta (RobboTotalBase* tb, int rf)
{
  uint64 r, u = rf * tb->PawnMult, v = u + tb->PawnMult;
  for (r = u; r < v; r += TOTAL_CACHE_PER)
    scopPILA (tb, r);
}
